home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / DTS.Lib / ListControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-02  |  46.2 KB  |  1,829 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         listcontrol.c
  5. ** Written by:      Eric Soldan
  6. **
  7. ** Copyright © 1991 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /*
  20. **
  21. ** To create a List control, you only need a single call.  For example:
  22. **
  23. **    list = CLNew(rViewCtl,                                    Resource ID of view control for List control.
  24. **                 true,                                         Control initially visible.
  25. **                 &viewRct,                                    View rect of list.
  26. **                 numRows,                                    Number of rows to create List with.
  27. **                 numCols,                                    Number of columns to create List with.
  28. **                 cellHeight,
  29. **                 cellWidth,
  30. **                 theLProc,                                    Custom List procedure resource ID.
  31. **                 window,                                    Window to hold List control.
  32. **                 clHScroll | clShowActive | clActive        Horizontal scrollbar, active List.
  33. **    );
  34. **
  35.  
  36. ** If the CLNew call succeeds, you then have a List control in your
  37. ** window.  It will be automatically disposed of when you close the window.
  38. ** If you don't want this to happen, then you can detach it from the
  39. ** view control which owns it.  To do this, you would to the following:
  40. **
  41. **  viewCtl = CLViewFromList(theListHndl);
  42. **  if (viewCtl) SetControlReference(viewCtl, nil);
  43. **
  44. ** The view control keeps a reference to the List record in the refCon.
  45. ** If the refCon is cleared, then the view control does nothing.  So, all that
  46. ** is needed to detach a List record from a view control is to set the
  47. ** view control's refCon nil.  Now if you close the window, you will still
  48. ** have the List record.
  49. **
  50. **
  51. ** To remove a List control completely from a window, just dispose of the view
  52. ** control that holds the List record.  To do this, just do something like the below:
  53. **
  54. **  DisposeControl(CLViewFromList(theListHndl));
  55. **
  56. ** This completely disposes of the List control.
  57. **
  58. **
  59. ** Events for the List record are handled nearly automatically.  Just make the
  60. ** following call:
  61. **
  62. **  CLClick(window, eventPtr, &action);
  63. **
  64. ** If the event was handled, true is returned.  If the event is false, then the
  65. ** event doesn't belong to a List control, and further processing of the event
  66. ** should be done.
  67. */
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. #ifndef __CONTROLS__
  76. #include <Controls.h>
  77. #endif
  78.  
  79. #ifndef __DTSLib__
  80. #include "DTS.Lib.h"
  81. #endif
  82.  
  83. #ifndef __ERRORS__
  84. #include <Errors.h>
  85. #endif
  86.  
  87. #ifndef __LISTCONTROL__
  88. #include "ListControl.h"
  89. #endif
  90.  
  91. #ifndef __LOWMEM__
  92. #include <LowMem.h>
  93. #endif
  94.  
  95. #ifndef __MEMORY__
  96. #include <Memory.h>
  97. #endif
  98.  
  99. #ifndef __PACKAGES__
  100. #include <Packages.h>
  101. #endif
  102.  
  103. #ifndef __RESOURCES__
  104. #include <Resources.h>
  105. #endif
  106.  
  107. #ifndef __TEXTUTILS__
  108. #include "TextUtils.h"
  109. #endif
  110.  
  111. #ifndef __UTILITIES__
  112. #include "Utilities.h"
  113. #endif
  114.  
  115.  
  116.  
  117. /*****************************************************************************/
  118.  
  119.  
  120.  
  121. #define kListPosTextLen 32
  122. #define kPrevSel        16
  123.  
  124. #ifdef powerc
  125. #pragma options align=mac68k
  126. #endif
  127. typedef struct cdefRsrcJMP {
  128.     long    jsrInst;
  129.     long    moveInst;
  130.     short    jmpInst;
  131.     long    jmpAddress;
  132. } cdefRsrcJMP;
  133. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  134. #ifdef powerc
  135. #pragma options align=reset
  136. #endif
  137.  
  138.  
  139.  
  140. /*****************************************************************************/
  141.  
  142.  
  143.  
  144. short    gListCtl = rListCtl;
  145.  
  146. static long                        gLastKeyTime;
  147. static char                        gListPosText[kListPosTextLen];
  148. static short                    gListPosTextLen;
  149. static cdefRsrcJMPHndl            gCDEF;
  150. static CLGetCompareDataProcPtr    gGetCompareDataProc;
  151. static CLDoCompareDataProcPtr    gDoCompareDataProc;
  152. static ListHandle                gCLVList;
  153. static ControlHandle            gVFLView;
  154. static ListHandle                gVFLList;
  155. static Rect                        gLRect;
  156.  
  157. static void            CLVScrollContent(short h, short v);
  158. static WindowPtr    CLVSetClip(ListHandle list);
  159.  
  160. static void            CLInitialize(void);
  161. static void            CLBorderDraw(ListHandle listHndl);
  162. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm);
  163. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen);
  164.  
  165. static void                dummyCLActivate(Boolean makeActive, ListHandle listHndl);
  166. static Boolean            dummyCLClick(WindowPtr window, EventRecord *event, short *action);
  167. static ControlHandle    dummyCLCtlHit(void);
  168. static ListHandle        dummyCLFindActive(WindowPtr window);
  169. static Boolean            dummyCLKey(WindowPtr window, EventRecord *event);
  170. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive);
  171. static ControlHandle    dummyCLViewFromList(ListHandle listHndl);
  172. static ListHandle        dummyCLWindActivate(WindowPtr window, Boolean displayIt);
  173.  
  174. CLActivateProcPtr        gclActivate       = dummyCLActivate;
  175. CLClickProcPtr            gclClick          = dummyCLClick;
  176. CLCtlHitProcPtr            gclCtlHit         = dummyCLCtlHit;
  177. CLFindActiveProcPtr        gclFindActive     = dummyCLFindActive;
  178. CLKeyProcPtr            gclKey            = dummyCLKey;
  179. CLNextProcPtr            gclNext           = dummyCLNext;
  180. CLViewFromListProcPtr    gclViewFromList   = dummyCLViewFromList;
  181. CLWindActivateProcPtr    gclWindActivate   = dummyCLWindActivate;
  182.  
  183.  
  184. CLVVariableSizeCellsProcPtr    gclvVariableSizeCells = nil;
  185. CLVGetCellRectProcPtr        gclvGetCellRect       = nil;
  186. CLVUpdateProcPtr            gclvUpdate            = nil;
  187. CLVAutoScrollProcPtr        gclvAutoScroll        = nil;
  188. CLVSetSelectProcPtr            gclvSetSelect         = nil;
  189. CLVClickProcPtr                gclvClick             = nil;
  190. CLVAdjustScrollBarsProcPtr  gclvAdjustScrollBars  = nil;
  191.  
  192.  
  193. extern short    gPrintPage;        /* Non-zero means we are printing. */
  194.  
  195.  
  196.  
  197. /*****************************************************************************/
  198.  
  199.  
  200.  
  201. static ListHandle        gFoundLHndl;
  202.     /* Global value used to return info from the List control proc. */
  203.  
  204. static ControlHandle    gFoundViewCtl;
  205.     /* Global value used to return info from the List control proc. */
  206.  
  207.  
  208.  
  209. /*****************************************************************************/
  210. /*****************************************************************************/
  211. /*****************************************************************************/
  212.  
  213.  
  214.  
  215. /* Instead of calling the functions directly, you can reference the global
  216. ** proc pointers that reference the functions.  This keeps everything from
  217. ** being linked in.  The default global proc pointers point to dummy functions
  218. ** that behave as if there aren't any list controls.  The calls can still be
  219. ** made, yet the runtime behavior is such that it will operate as if there
  220. ** no instances of the List control.  This allows intermediate code to access
  221. ** the functions or not without automatically linking in all sorts of stuff
  222. ** into the application that isn't desired.  To change the global proc pointers
  223. ** so that they point to the actual functions, just call CLInitialize() once
  224. ** in the beginning of the application.  If CLInitialize() is referenced, it will
  225. ** get linked in.  In turn, everything that it references directly or indirectly
  226. ** will get linked in. */
  227.  
  228. #pragma segment ListControl
  229. static void    CLInitialize(void)
  230. {
  231.     if (gclActivate != CLActivate) {
  232.         gclActivate       = CLActivate;
  233.         gclClick          = CLClick;
  234.         gclCtlHit         = CLCtlHit;
  235.         gclFindActive     = CLFindActive;
  236.         gclKey            = CLKey;
  237.         gclNext           = CLNext;
  238.         gclViewFromList   = CLViewFromList;
  239.         gclWindActivate   = CLWindActivate;
  240.     }
  241. }
  242.  
  243.  
  244.  
  245. /*****************************************************************************/
  246.  
  247.  
  248.  
  249. /* Activate this List record.  Activation is NOT done by calling LActivate().
  250. ** The active control is indicated by the 2-pixel thick border around the
  251. ** List control.  This allows all List controls in a window to display which
  252. ** cells are selected.  This behavior can be overridden by calling LActivate()
  253. ** on the List record for List controls.
  254. ** Human interface dictates that only at most a single List control has this
  255. ** active border.  For this reason, this function scans for other List
  256. ** controls in the window and removes the border from any other that it finds. */
  257.  
  258. #pragma segment ListControl
  259. void    CLActivate(Boolean makeActive, ListHandle listHndl)
  260. {
  261.     WindowPtr        window, oldPort;
  262.     ControlHandle    viewCtl;
  263.     short            oldDisplay, newDisplay;
  264.     ListHandle        list;
  265.     CLDataHndl        listData;
  266.  
  267.     if (listHndl) {
  268.         window = (WindowPtr)(*listHndl)->port;
  269.         for (viewCtl = nil;;) {
  270.             viewCtl = CLNext(window, &list, viewCtl, 1, false);
  271.             if (!viewCtl) break;
  272.             listData   = (CLDataHndl)(*viewCtl)->contrlData;
  273.             oldDisplay = (*listData)->mode;
  274.             newDisplay = (oldDisplay & (0xFFFF - clActive));
  275.             if (makeActive)
  276.                 if (list == listHndl)
  277.                     newDisplay |= clActive;
  278.             if (oldDisplay != newDisplay) {
  279.                 (*listData)->mode = newDisplay;
  280.                 GetPort(&oldPort);
  281.                 SetPort(window);
  282.                 CLBorderDraw(list);
  283.                 SetPort(oldPort);
  284.             }
  285.         }
  286.     }
  287. }
  288.  
  289.  
  290.  
  291. static void    dummyCLActivate(Boolean makeActive, ListHandle listHndl)
  292. {
  293. #ifndef __MWERKS__
  294. #pragma unused (makeActive, listHndl)
  295. #endif
  296. }
  297.  
  298.  
  299.  
  300. /*****************************************************************************/
  301.  
  302.  
  303.  
  304. #pragma segment ListControl
  305. static void    CLBorderDraw(ListHandle listHndl)
  306. {
  307.     ControlHandle    viewCtl;
  308.     WindowPtr        oldPort, listPort;
  309.     short            displayInfo;
  310.     PenState        oldPen;
  311.     CLDataHndl        listData;
  312.  
  313.     SetRect(&gLRect, 0, 0, 0, 0);
  314.     if (listHndl) {
  315.         viewCtl = CLViewFromList(listHndl);
  316.         if (viewCtl) {
  317.             if ((*viewCtl)->contrlVis) {
  318.                 GetPort(&oldPort);
  319.                 SetPort(listPort = (*listHndl)->port);
  320.                 GetPenState(&oldPen);
  321.                 PenNormal();
  322.                 listData    = (CLDataHndl)(*viewCtl)->contrlData;
  323.                 displayInfo = (*listData)->mode;
  324.                 gLRect = (*listHndl)->rView;
  325.                 InsetRect(&gLRect, -1, -1);
  326.                 FrameRect(&gLRect);
  327.                 if (displayInfo & clShowActive) {
  328.                     gLRect = (*listHndl)->rView;
  329.                     InsetRect(&gLRect, -4, -4);
  330.                     if ((*listHndl)->vScroll)
  331.                         gLRect.right  += 15;
  332.                     if ((*listHndl)->hScroll)
  333.                         gLRect.bottom += 15;
  334.                     PenSize(2, 2);
  335.                     if ((!((WindowPeek)listPort)->hilited) || (!(displayInfo & clActive)))
  336.                         PenPat((ConstPatternParam)&qd.white);
  337.                     FrameRect(&gLRect);
  338.                 }
  339.                 SetPenState(&oldPen);
  340.                 SetPort(oldPort);
  341.             }
  342.         }
  343.     }
  344. }
  345.  
  346.  
  347.  
  348. /*****************************************************************************/
  349.  
  350.  
  351.  
  352. /* This is called when a mouseDown occurs in the content of a window.  It
  353. ** returns true if the mouseDown caused a List action to occur.  Events
  354. ** that are handled include if the user clicks on a scrollbar that is
  355. ** associated with a List control. */
  356.  
  357. #pragma segment ListControl
  358. Boolean    CLClick(WindowPtr window, EventRecord *event, short *action)
  359. {
  360.     WindowPtr        oldPort;
  361.     Point            mouseLoc;
  362.     ListHandle        list;
  363.     ControlHandle    ctlHit, viewCtl;
  364.     CLDataHndl        listData;
  365.     short            mode;
  366.     unsigned char    ohlt;
  367.  
  368.     if (action)
  369.         *action = 0;
  370.     gLastKeyTime = 0;
  371.  
  372.     GetPort(&oldPort);
  373.     if (!((WindowPeek)window)->hilited) return(false);
  374.  
  375.     SetPort(window);
  376.     mouseLoc = event->where;
  377.     GlobalToLocal(&mouseLoc);
  378.  
  379.     if (!(viewCtl = CLFindCtl(window, event, &list, &ctlHit))) return(false);
  380.  
  381.     if (!list) {
  382.         SetPort(oldPort);
  383.         return(false);
  384.     }        /* Didn't hit list control or related scrollbar.  No action taken. */
  385.  
  386.     if (CLFindActive(window) != list) {            /* If not active list control, activate it.       */
  387.         if ((*viewCtl)->contrlHilite != 255) {
  388.             CLActivate(true, list);                /* Now is the active list control.               */
  389.             if (action)                            /* CLClick can be called again if the control  */
  390.                 *action = -1;                    /* activates and operates with the same click. */
  391.             SetPort(oldPort);
  392.             return(true);
  393.         }
  394.     }
  395.  
  396.     UseControlStyle(viewCtl);
  397.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  398.     mode     = (*listData)->mode;
  399.     if (mode & clVariable) {
  400.         if ((*gclvClick)(mouseLoc, event->modifiers, list))
  401.             if (action)
  402.                 *action = 1;        /* If double-click, then return that it was. */
  403.     }
  404.     else {
  405.         ohlt = (*viewCtl)->contrlHilite;
  406.         (*viewCtl)->contrlHilite = -1;
  407.             /* System 6 has a bug.  It calls FindControl to see if a List scrollbar was hit.  If it finds a control,
  408.             ** it assumes that it is going to be a list control of its own.  If it can't match against either the vert
  409.             ** or horiz scrollbar, it exits.  This is of course really bad for a list control, since the body of the
  410.             ** list is a control, and therefore FindControl will return something that doesn't match the scrollbars.
  411.             ** This only fixes the problem if the list control isn't on top of any other controls.  Controls beneath
  412.             ** the list will need to be temporarily inactivated by the application. */
  413.  
  414.         if (LClick(mouseLoc, event->modifiers, list))
  415.             if (action)
  416.                 *action = 1;        /* If double-click, then return that it was. */
  417.         (*viewCtl)->contrlHilite = ohlt;
  418.     }
  419.     UseControlStyle(nil);
  420.  
  421.     SetPort(oldPort);
  422.     return(true);
  423. }
  424.  
  425.  
  426.  
  427. static Boolean    dummyCLClick(WindowPtr window, EventRecord *event, short *action)
  428. {
  429. #ifndef __MWERKS__
  430. #pragma unused (window, event)
  431. #endif
  432.  
  433.     if (action)
  434.         *action = 0;
  435.     return(false);
  436. }
  437.  
  438.  
  439.  
  440. /*****************************************************************************/
  441.  
  442.  
  443.  
  444. #pragma segment ListControl
  445. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm)
  446. {
  447. #ifndef __MWERKS__
  448. #pragma unused (varCode)
  449. #endif
  450.  
  451.     Rect            viewRct;
  452.     ListHandle        list;
  453.     WindowPtr        curPort, ww;
  454.     ControlHandle    vScroll, hScroll;
  455.     CLDataHndl        listData;
  456.  
  457.     list = (ListHandle)GetControlReference(ctl);
  458.     if (list)
  459.         viewRct = (*list)->rView;
  460.     else
  461.         SetRect(&viewRct, 0, 0, 0, 0);
  462.  
  463.     switch (msg) {
  464.         case drawCntl:
  465.             GetPort(&curPort);
  466.             vScroll = (*list)->vScroll;
  467.             if (vScroll) {
  468.                 ww = (*vScroll)->contrlOwner;
  469.                 if (!((WindowPeek)ww)->hilited) (*list)->vScroll = nil;
  470.             }
  471.             hScroll = (*list)->hScroll;
  472.             if (hScroll) {
  473.                 ww = (*hScroll)->contrlOwner;
  474.                 if (!((WindowPeek)ww)->hilited) (*list)->hScroll = nil;
  475.             }
  476.             CLUpdate(curPort->visRgn, list);
  477.             (*list)->vScroll = vScroll;
  478.             (*list)->hScroll = hScroll;
  479.             CLBorderDraw(list);
  480.             break;
  481.  
  482.         case testCntl:
  483.             if (PtInRect(*(Point *)&parm, &viewRct)) {
  484.                 gFoundViewCtl = ctl;
  485.                 gFoundLHndl   = list;
  486.                 return(1);
  487.             }
  488.             return(0);
  489.             break;
  490.  
  491.         case calcCRgns:
  492.         case calcCntlRgn:
  493.             if (msg == calcCRgns)
  494.                 parm &= 0x00FFFFFF;
  495.             RectRgn((RgnHandle)parm, &viewRct);
  496.             break;
  497.  
  498.         case initCntl:
  499.             break;
  500.  
  501.         case dispCntl:
  502.             if (list) {
  503.                 GetPort(&curPort);
  504.                 SetPort((*list)->port);
  505.                 gVFLView = ctl;
  506.                 gVFLList = list;
  507.                 listData = (CLDataHndl)(*ctl)->contrlData;
  508.                 (*listData)->mode &= (0xFFFF - clActive);
  509.                 CLBorderDraw(list);
  510.                 EraseRect(&gLRect);
  511.                 InvalRect(&gLRect);
  512.                 gVFLView = nil;
  513.                 gVFLList = nil;
  514.                 LDispose(list);
  515.                 DisposeHandle((Handle)(*ctl)->contrlData);
  516.                 SetPort(curPort);
  517.             }
  518.             break;
  519.  
  520.         case posCntl:
  521.             break;
  522.  
  523.         case thumbCntl:
  524.             break;
  525.  
  526.         case dragCntl:
  527.             break;
  528.  
  529.         case autoTrack:
  530.             break;
  531.     }
  532.  
  533.     return(0);
  534. }
  535.  
  536.  
  537.  
  538. /*****************************************************************************/
  539.  
  540.  
  541.  
  542. /* The List control that was hit by calling FindControl is saved in a
  543. ** global variable, since the CDEF has no way of returning what kind it was.
  544. ** To determine that it was a List control that was hit, first call this
  545. ** function.  The first call returns the old value in the global variable,
  546. ** plus it resets the global to nil.  Then call FindControl(), and then
  547. ** call this function again.  If it returns nil, then a List control
  548. ** wasn't hit.  If it returns non-nil, then it was a List control that
  549. ** was hit, and specifically the one returned. */
  550.  
  551. #pragma segment ListControl
  552. ControlHandle    CLCtlHit(void)
  553. {
  554.     ControlHandle    ctl;
  555.  
  556.     ctl = gFoundViewCtl;
  557.     gFoundViewCtl = nil;
  558.     return(ctl);
  559. }
  560.  
  561.  
  562.  
  563. static ControlHandle    dummyCLCtlHit(void)
  564. {
  565.     return(nil);
  566. }
  567.  
  568.  
  569.  
  570. /*****************************************************************************/
  571.  
  572.  
  573.  
  574. /* Handle the event if it applies to the active List control.  If some
  575. ** action occured due to the event, return true. */
  576.  
  577. #pragma segment ListControl
  578. Boolean    CLEvent(WindowPtr window, EventRecord *event, short *action)
  579. {
  580.     WindowPtr    clickWindow;
  581.     short        actn;
  582.  
  583.     if (action)
  584.         *action = 0;
  585.  
  586.     switch(event->what) {
  587.  
  588.         case mouseDown:
  589.             if (FindWindow(event->where, &clickWindow) == inContent)
  590.                 if (window == clickWindow)
  591.                     if (((WindowPeek)window)->hilited) return(CLClick(window, event, action));
  592.             break;
  593.  
  594.         case autoKey:
  595.         case keyDown:
  596.             if (!(event->modifiers & cmdKey)) {
  597.                 actn = CLKey(window, event);
  598.                 if (action)
  599.                     *action = actn;
  600.                 if (actn) return(true);
  601.             }
  602.             break;
  603.     }
  604.  
  605.     return(false);
  606. }
  607.  
  608.  
  609.  
  610. /*****************************************************************************/
  611.  
  612.  
  613.  
  614. /* Returns the active List control, if any.  Unlike the TextEdit control, passing
  615. ** in nil doesn't return the currently active control independent of window.  The
  616. ** only reason that the TextEdit control returns the "globally active" control is
  617. ** so that the TEIdle procedure can do its thing.  The List control doesn't have
  618. ** such a thing, so there is no purpose for a "globally active" control.  If the
  619. ** window pointer passed in is nil (requesting the "globally active" List control),
  620. ** we just return nil, indicating that there isn't one. */
  621.  
  622. #pragma segment ListControl
  623. ListHandle    CLFindActive(WindowPtr window)
  624. {
  625.     ControlHandle    viewCtl;
  626.     ListHandle        list;
  627.     short            display;
  628.     CLDataHndl        listData;
  629.  
  630.     if (!window) return(nil);
  631.  
  632.     for (viewCtl = nil;;) {
  633.         viewCtl = CLNext(window, &list, viewCtl, 1, true);
  634.         if (!viewCtl) break;
  635.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  636.         display  = (*listData)->mode;
  637.         if (display & clActive) break;
  638.     }
  639.     return(list);
  640. }
  641.  
  642.  
  643.  
  644. static ListHandle    dummyCLFindActive(WindowPtr window)
  645. {
  646. #ifndef __MWERKS__
  647. #pragma unused (window)
  648. #endif
  649.     return(nil);
  650. }
  651.  
  652.  
  653.  
  654. /*****************************************************************************/
  655.  
  656.  
  657.  
  658. /* This determines if a List control was clicked on directly.  This does
  659. ** not determine if a related scrollbar was clicked on.  If a List
  660. ** control was clicked on, then true is returned, as well as the List
  661. ** handle and the handle to the view control. */
  662.  
  663. #pragma segment ListControl
  664. ControlHandle    CLFindCtl(WindowPtr window, EventRecord *event, ListHandle *listHndl, ControlHandle *ctlHit)
  665. {
  666.     WindowPtr        oldPort;
  667.     Point            mouseLoc;
  668.     ControlHandle    ctl, listctl;
  669.     ListHandle        list;
  670.  
  671.     if (window) {
  672.         GetPort(&oldPort);
  673.         SetPort(window);
  674.         mouseLoc = event->where;
  675.         GlobalToLocal(&mouseLoc);
  676.         SetPort(oldPort);
  677.  
  678.         gFoundLHndl = nil;
  679.  
  680.         if (!WhichControl(mouseLoc, 0, window, &ctl)) return(nil);
  681.             /* Didn't hit a thing, so forget it. */
  682.  
  683.         list = CLFromScroll(ctl, &listctl);
  684.         if (list) {
  685.             if (ctlHit)
  686.                 *ctlHit = ctl;
  687.             if (listHndl)
  688.                 *listHndl = list;
  689.             return(listctl);
  690.         }
  691.  
  692.         FindControl(mouseLoc, window, &ctl);
  693.         if (!ctl)                                                        return(nil);
  694.         if (StripAddress((*ctl)->contrlDefProc) != StripAddress(gCDEF)) return(nil);
  695.             /* Control hit was above List control, so we didn't hit a List control. */
  696.         if (ctlHit)
  697.             *ctlHit = ctl;
  698.         if (listHndl)
  699.             *listHndl = gFoundLHndl;
  700.         if (gFoundLHndl) return(ctl);
  701.     }
  702.  
  703.     if (listHndl)
  704.         *listHndl = nil;
  705.     if (ctlHit)
  706.         *ctlHit = nil;
  707.     return(nil);
  708. }
  709.  
  710.  
  711.  
  712. /*****************************************************************************/
  713.  
  714.  
  715.  
  716. /* Find the List record that is related to the indicated scrollbar. */
  717.  
  718. #pragma segment ListControl
  719. ListHandle    CLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  720. {
  721.     WindowPtr        window;
  722.     ControlHandle    viewCtl;
  723.     ListHandle        list;
  724.  
  725.     *retCtl = nil;
  726.     if (!IsScrollBar(scrollCtl)) return(nil);
  727.  
  728.     window = (*scrollCtl)->contrlOwner;
  729.  
  730.     for (*retCtl = viewCtl = nil;;) {
  731.         viewCtl = CLNext(window, &list, viewCtl, 1, false);
  732.         if (!viewCtl) return(nil);
  733.         list = (ListHandle)GetControlReference(viewCtl);
  734.         if (
  735.             ((*list)->vScroll == scrollCtl) || 
  736.             ((*list)->hScroll == scrollCtl)
  737.         ) {
  738.             *retCtl = viewCtl;
  739.             return(list);
  740.         }
  741.     }
  742. }
  743.  
  744.  
  745.  
  746. /*****************************************************************************/
  747.  
  748.  
  749.  
  750. /* Get the Nth List control in the control list of a window. */
  751.  
  752. #pragma segment ListControl
  753. ListHandle    CLGetList(WindowPtr window, short lnum)
  754. {
  755.     ControlHandle    ctl;
  756.     ListHandle        list;
  757.  
  758.     for (ctl = nil; lnum--; ctl = CLNext(window, &list, ctl, 1, false)) {};
  759.     return(list);
  760. }
  761.  
  762.  
  763.  
  764. /*****************************************************************************/
  765.  
  766.  
  767.  
  768. /* Insert a cell alphabetically into the list.  Whichever parameter is passed in
  769. ** as -1, either row or column, that is the dimension that is determined. */
  770.  
  771. #pragma segment ListControl
  772. short    CLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col)
  773. {
  774.     short            loc, len;
  775.     Point            cell;
  776.     char            cstr[256];
  777.     CLDataHndl        listData;
  778.     ControlHandle    viewCtl;
  779.  
  780.     if (!listHndl) return(-1);
  781.  
  782.     gGetCompareDataProc = nil;
  783.     viewCtl = CLViewFromList(listHndl);
  784.     if (viewCtl) {
  785.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  786.         if (listData)
  787.             gGetCompareDataProc = (*listData)->getCompareData;
  788.     }
  789.  
  790.     if (gGetCompareDataProc) {
  791.         (*gGetCompareDataProc)(data, dataLen, cstr, &len);
  792.         loc = CLRowOrColSearch(listHndl, cstr, len, row, col);
  793.     }
  794.     else loc = CLRowOrColSearch(listHndl, data, dataLen, row, col);
  795.  
  796.     if (row == -1) {
  797.         LAddRow(1, cell.v = loc, listHndl);
  798.         cell.h = col;
  799.     }
  800.     else {
  801.         LAddColumn(1, cell.h = loc, listHndl);
  802.         cell.v = row;
  803.     }
  804.  
  805.     LSetCell(data, dataLen, cell, listHndl);
  806.     return(loc);
  807. }
  808.  
  809.  
  810.  
  811. /*****************************************************************************/
  812.  
  813.  
  814.  
  815. /* See if the keypress event applies to the List control, and if it does,
  816. ** handle it and return true. */
  817.  
  818. #pragma segment ListControl
  819. Boolean    CLKey(WindowPtr window, EventRecord *event)
  820. {
  821.     ListHandle            list;
  822.     ControlHandle        listCtl;
  823.     short                key, mode, thresh;
  824.     long                ll;
  825.     Point                cell, dcell, mm, oldCell, lastCell;
  826.     Rect                bnds, visCells, rct;
  827.     CLDataHndl            listData;
  828.     CLKeyFilterProcPtr    kproc;
  829.  
  830.     list = CLFindActive(window);
  831.     if (list) {
  832.  
  833.         listCtl  = CLViewFromList(list);
  834.         listData = (CLDataHndl)(*listCtl)->contrlData;
  835.         mode     = (*listData)->mode;
  836.         if (!(mode & clKeyPos)) return(false);
  837.  
  838.         kproc = (*listData)->keyFilter;
  839.         if (kproc)
  840.             if ((*kproc)(list, event))
  841.                 return(true);
  842.  
  843.         bnds = (*list)->dataBounds;
  844.         if (bnds.top == bnds.bottom) return(true);
  845.         if (bnds.left == bnds.right) return(true);
  846.             /* The list is empty, so whatever was typed has been "handled". */
  847.  
  848.         if (mode & clVariable) {
  849.             ++bnds.top;
  850.             ++bnds.left;
  851.         }
  852.  
  853.         cell.h = bnds.left;
  854.         cell.v = bnds.top;
  855.         key = event->message & charCodeMask;
  856.  
  857.         if ((key >= chLeft) && (key <= chDown)) {
  858.             if (LGetSelect(true, &cell, list)) key -= kPrevSel;
  859.             else                               cell.h = cell.v = -1;
  860.             for (oldCell = lastCell = cell;; lastCell = cell) {
  861.                 switch (key) {
  862.                     case chLeft - kPrevSel:
  863.                         if (cell.h > bnds.left)
  864.                             --cell.h;
  865.                         break;
  866.                     case chRight - kPrevSel:
  867.                         if (cell.h < bnds.right - 1)
  868.                             ++cell.h;
  869.                         break;
  870.                     case chUp - kPrevSel:
  871.                         if (cell.v > bnds.top)
  872.                             --cell.v;
  873.                         break;
  874.                     case chDown - kPrevSel:
  875.                         if (cell.v < bnds.bottom - 1)
  876.                             ++cell.v;
  877.                         break;
  878.                     case chLeft:
  879.                         cell.v = (*list)->visible.top;
  880.                         cell.h = bnds.right - 1;
  881.                         break;
  882.                     case chRight:
  883.                         cell.v = (*list)->visible.top;
  884.                         cell.h = bnds.left;
  885.                         break;
  886.                     case chUp:
  887.                         cell.h = (*list)->visible.left;
  888.                         cell.v = bnds.bottom - 1;
  889.                         break;
  890.                     case chDown:
  891.                         cell.h = (*list)->visible.left;
  892.                         cell.v = bnds.top;
  893.                         break;
  894.                 }
  895.                 if (!(mode & clVariable)) break;
  896.                 if ((cell.h == lastCell.h) && (cell.v == lastCell.v)) break;
  897.                 (*gclvGetCellRect)(list, cell.h, cell.v, &rct);
  898.                 if (!EmptyRect(&rct)) break;
  899.                 if (key > (chDown - kPrevSel)) key -= kPrevSel;
  900.             }
  901.             if (mode & clVariable) {
  902.                 (*gclvGetCellRect)(list, cell.h, cell.v, &rct);
  903.                 if (EmptyRect(&rct)) cell = oldCell;
  904.                 if (cell.h == -1) return(true);
  905.             }
  906.         }
  907.         else {
  908.             thresh = LMGetKeyThresh();;
  909.             if (thresh > 60) thresh = 60;
  910.             thresh <<= 1;
  911.             if (gLastKeyTime + thresh < event->when)        /* Too long, reset char collection. */
  912.                 gListPosTextLen = 0;
  913.             gLastKeyTime = event->when;
  914.             if (gListPosTextLen < kListPosTextLen)
  915.                 gListPosText[gListPosTextLen++] = key;
  916.             gGetCompareDataProc = (*listData)->getCompareData;
  917.             gDoCompareDataProc  = (*listData)->doCompareData;
  918.             if (!LGetSelect(true, &cell, list)) cell.h = (*list)->visible.left;
  919.             cell.v = CLRowOrColSearch(list, gListPosText, gListPosTextLen, -1, cell.h);
  920.             if (cell.v >= bnds.bottom) cell.v = bnds.bottom - 1;
  921.         }
  922.  
  923.         UseControlStyle(listCtl);
  924.  
  925.         if (mode & clVariable)
  926.             (*gclvSetSelect)(true, cell, list);
  927.         else
  928.             LSetSelect(true, cell, list);        /* Select cell that is closest. */
  929.  
  930.         dcell.h = bnds.left;
  931.         dcell.v = bnds.top;
  932.         for (;;) {                /* Deselect old cells. */
  933.             if (!LGetSelect(true, &dcell, list)) break;
  934.             if ((dcell.h == cell.h) && (dcell.v == cell.v)) {
  935.                 if (++dcell.h >= bnds.right) {
  936.                     dcell.h = bnds.left;
  937.                     ++dcell.v;
  938.                 }
  939.             }
  940.             else {
  941.                 if (mode & clVariable)
  942.                     (*gclvSetSelect)(false, dcell, list);
  943.                 else
  944.                     LSetSelect(false, dcell, list);
  945.             }
  946.         }
  947.         UseControlStyle(nil);
  948.  
  949.         visCells = (*list)->visible;
  950.         if (PtInRect(cell, &visCells)) return(true);        /* Already in view. */
  951.  
  952.         UseControlStyle(listCtl);            /* Scroll into view the way we want it done. */
  953.         if (mode & clVariable) (*gclvAutoScroll)(list);
  954.         else {
  955.             ll = PinRect(&visCells, cell);
  956.             mm = *(Point *)≪
  957.             LScroll(cell.h - mm.h, cell.v - mm.v, list);
  958.         }
  959.         UseControlStyle(nil);
  960.         return(true);
  961.     }
  962.  
  963.     return(false);
  964. }
  965.  
  966.  
  967.  
  968. static Boolean    dummyCLKey(WindowPtr window, EventRecord *event)
  969. {
  970. #ifndef __MWERKS__
  971. #pragma unused (window, event)
  972. #endif
  973.     return(false);
  974. }
  975.  
  976.  
  977.  
  978. /*****************************************************************************/
  979.  
  980.  
  981.  
  982. /* Create a new List control.  See the comments at the beginning of this
  983. ** file for more information.  Note that this function doesn't get a dummy,
  984. ** as you really mean to have this code if you call it.  It calls CLInitialize(),
  985. ** just to make sure that the proc pointers are set to point to the real function,
  986. ** instead of the dummy functions.  By having CLNew() call CLInitialize(), the
  987. ** application won't have to worry about calling CLInitialize().  By the time that
  988. ** the application is using a List control, the proc pointers will be initialized. */
  989.  
  990. #pragma segment ListControl
  991. ListHandle    CLNew(short viewID, Boolean vis, Rect *vrct, short numRows, short numCols,
  992.                   short cellHeight, short cellWidth, short theLProc, WindowPtr window, short mode)
  993. {
  994.     WindowPtr        oldPort;
  995.     Rect            viewRct, dataBnds, rct;
  996.     Point            cellSize;
  997.     ListHandle        list;
  998.     Boolean            err;
  999.     ControlHandle    viewCtl;
  1000.     Boolean            drawIt, hasGrow;
  1001.     short            hScroll, vScroll;
  1002.     CLDataHndl        listData;
  1003.     ControlHandle    ctl;
  1004.     RgnHandle        oldClip, newClip;
  1005.  
  1006.     static ControlDefUPP    cdefupp;
  1007.  
  1008.     CLInitialize();            /* Make sure that this code gets linked in, in case the application
  1009.                             ** is using the proc pointers.  CtlHandler.c uses the proc pointers
  1010.                             ** so that if the application doesn't use List controls, this code
  1011.                             ** won't get linked in. */
  1012.     GetPort(&oldPort);
  1013.     SetPort(window);
  1014.  
  1015.     viewRct         = *vrct;
  1016.     dataBnds.top    = dataBnds.left = 0;
  1017.     dataBnds.right  = numCols;
  1018.     dataBnds.bottom = numRows;
  1019.  
  1020.     cellSize.h = cellWidth;
  1021.     cellSize.v = cellHeight;
  1022.  
  1023.     drawIt  = (mode & clDrawIt)  ? 1 : 0;
  1024.     hScroll = (mode & clHScroll) ? 1 : 0;
  1025.     vScroll = (mode & clVScroll) ? 1 : 0;
  1026.     hasGrow = (mode & clHasGrow) ? 1 : 0;
  1027.  
  1028.     viewCtl = nil;
  1029.  
  1030.     if (!vis) {
  1031.         GetClip(oldClip = NewRgn());
  1032.         SetClip(newClip = NewRgn());
  1033.     }
  1034.     list = LNew(&viewRct, &dataBnds, cellSize, theLProc, window, false, hasGrow, hScroll, vScroll);
  1035.     if (!vis) {
  1036.         if (list) {
  1037.             if ((*list)->hScroll) HideControl((*list)->hScroll);
  1038.             if ((*list)->vScroll) HideControl((*list)->vScroll);
  1039.         }
  1040.         SetClip(oldClip);
  1041.         DisposeRgn(oldClip);
  1042.         DisposeRgn(newClip);
  1043.     }
  1044.  
  1045.     err = false;
  1046.     if (list) {        /* If we were able to create the List record... */
  1047.  
  1048.         if ((hasGrow) && (hScroll + vScroll == 1)) {
  1049.             ctl = (*list)->hScroll;
  1050.             if (ctl) {
  1051.                 rct = (*ctl)->contrlRect;
  1052.                 if (rct.right >= viewRct.right)
  1053.                     SizeControl(ctl, viewRct.right - viewRct.left - 13, rct.bottom - rct.top);
  1054.             }
  1055.             ctl = (*list)->vScroll;
  1056.             if (ctl) {
  1057.                 rct = (*ctl)->contrlRect;
  1058.                 if (rct.bottom >= viewRct.bottom)
  1059.                     SizeControl(ctl, rct.right - rct.left, viewRct.bottom - viewRct.top - 13);
  1060.             }
  1061.         }
  1062.  
  1063.         if (!gCDEF) {
  1064.             gCDEF = (cdefRsrcJMPHndl)GetResource('CDEF', (viewID / 16));
  1065.             if (gCDEF) {
  1066.                 if (!cdefupp) {
  1067.                     cdefupp = NewControlDefProc(CLCtl);
  1068.                 }
  1069.                 (*gCDEF)->jmpAddress = (long)cdefupp;
  1070.                 if (TrapExists(_HWPriv))
  1071.                     FlushInstructionCache();
  1072.                         /* Make sure that instruction caches don't kill us. */
  1073.             }
  1074.             else err = true;
  1075.         }
  1076.  
  1077.         if (!err)
  1078.             viewCtl = NewControl(window, &viewRct, "\p", false, 0, 0, 0, viewID, (long)list);
  1079.                 /* Use our custom view cdef.  It's wierd, but it's small. */
  1080.                 /* We have to create the control initially invisible because we haven't */
  1081.                 /* initialized all of the data needed by the update procedure.  If it   */
  1082.                 /* is created visible, the update procedure will be immediately called, */
  1083.                 /* and this would be very bad to do until all of the data is there.     */
  1084.  
  1085.         mode &= (0xFFFF - clDrawIt);
  1086.  
  1087.         if (!viewCtl) err = true;
  1088.  
  1089.         if (!err) {
  1090.             (*viewCtl)->contrlData = nil;
  1091.             listData = (CLDataHndl)NewHandleClear(sizeof(CLDataRec));
  1092.             if (listData) {
  1093.                 (*listData)->mode      = mode & (0xFFFF - clVariable);
  1094.                 (*viewCtl)->contrlData = (Handle)listData;
  1095.                 if (mode & clVariable)
  1096.                     if (gclvVariableSizeCells)
  1097.                         (*gclvVariableSizeCells)(list);
  1098.                 if (vis) {
  1099.                     LSetDrawingMode(drawIt, list);
  1100.                     if (vis) ShowStyledControl(viewCtl);
  1101.                         /* Now that the data is initialized, we can show the control. */
  1102.                 }
  1103.                 else
  1104.                     if (drawIt)
  1105.                         (*list)->listFlags &= (0xFFFF - 0x08);
  1106.                             /* The silly ListMgr shows the scrollbars if we just call LDoDraw. */
  1107.                 if (mode & clActive)
  1108.                     CLActivate(true, list);
  1109.             }
  1110.             else err = true;
  1111.         }
  1112.     }
  1113.     else err = true;
  1114.  
  1115.     SetPort(oldPort);
  1116.  
  1117.     if (err) {        /* Oops.  Somebody wasn't happy. */
  1118.         if (viewCtl)
  1119.             DisposeControl(viewCtl);
  1120.                 /* This also disposes of the List handle! */
  1121.         else
  1122.             if (list)
  1123.                 LDispose(list);
  1124.                     /* We have to dispose of the List handle ourselves if
  1125.                     ** creating the view control failed. */
  1126.  
  1127.         list = nil;        /* Return that there is no List control. */
  1128.     }
  1129.  
  1130.     return(list);
  1131. }
  1132.  
  1133.  
  1134.  
  1135. /*****************************************************************************/
  1136.  
  1137.  
  1138.  
  1139. /* Get the next List control in the window.  You pass it a control handle
  1140. ** for the view control, or nil to start at the beginning of the window.
  1141. ** It returns both a List handle and the view control handle for that
  1142. ** List record.  If none is found, nil is returned.  This allows you to
  1143. ** repeatedly call this function and walk through all the List controls
  1144. ** in a window. */
  1145.  
  1146. #pragma segment ListControl
  1147. ControlHandle    CLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive)
  1148. {
  1149.     ControlHandle    nextCtl, priorCtl;
  1150.  
  1151.     if (listHndl)
  1152.         *listHndl = nil;
  1153.  
  1154.     if (!window) return(nil);
  1155.     if (!gCDEF)  return(nil);
  1156.  
  1157.     if (dir > 0) {
  1158.         if (!ctl)
  1159.             ctl = ((WindowPeek)window)->controlList;
  1160.         else
  1161.             ctl = (*ctl)->nextControl;
  1162.         while (ctl) {
  1163.             if ((!justActive) || ((*ctl)->contrlVis)) {
  1164.                 if ((!justActive) || ((*ctl)->contrlHilite != 255)) {
  1165.                     if (StripAddress((*ctl)->contrlDefProc) == StripAddress(gCDEF)) {
  1166.                             /* The handle may be locked, which means that the hi-bit
  1167.                             ** may be on, thus invalidating the compare.  Dereference the
  1168.                             ** handles to get rid of this possibility. */
  1169.                         if (listHndl)
  1170.                             *listHndl = (ListHandle)GetControlReference(ctl);
  1171.                         return(ctl);
  1172.                     }
  1173.                 }
  1174.             }
  1175.             ctl = (*ctl)->nextControl;
  1176.         }
  1177.         return(ctl);
  1178.     }
  1179.  
  1180.     nextCtl = ((WindowPeek)window)->controlList;
  1181.     for (priorCtl = nil; ;nextCtl = (*nextCtl)->nextControl) {
  1182.         if ((!nextCtl) || (nextCtl == ctl)) {
  1183.             if (priorCtl)
  1184.                 if (listHndl)
  1185.                     *listHndl = (ListHandle)GetControlReference(priorCtl);
  1186.             return(priorCtl);
  1187.         }
  1188.         if ((!justActive) || ((*nextCtl)->contrlVis)) {
  1189.             if ((!justActive) || ((*nextCtl)->contrlHilite != 255)) {
  1190.                 if (StripAddress((*nextCtl)->contrlDefProc) == StripAddress(gCDEF))
  1191.                     priorCtl = nextCtl;
  1192.                         /* The handle may be locked, which means that the hi-bit
  1193.                         ** may be on, thus invalidating the compare.  Dereference the
  1194.                         ** handles to get rid of this possibility. */
  1195.             }
  1196.         }
  1197.     }
  1198. }
  1199.  
  1200.  
  1201.  
  1202. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive)
  1203. {
  1204. #ifndef __MWERKS__
  1205. #pragma unused (window, ctl, dir, justActive)
  1206. #endif
  1207.     *listHndl = nil;
  1208.     return(nil);
  1209. }
  1210.  
  1211.  
  1212.  
  1213. /*****************************************************************************/
  1214.  
  1215.  
  1216.  
  1217. /* From the starting for or column, print as many cells as will fit into the
  1218. ** designated rect.  Pass in a starting row and column, and they will be
  1219. ** adjusted to indicate the first cell that didn't fit into the rect.  If all
  1220. ** remaining cells were printed, the row is returned as -1.  The bottom of the
  1221. ** rect to print in is also adjusted to indicate where the actual cut-off
  1222. ** point was. */
  1223.  
  1224. #pragma segment ListControl
  1225. void    CLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col,
  1226.                 short leftEdge, Rect *drawRct)
  1227. {
  1228.     Rect        dataBnds, keepView, keepVis;
  1229.     Point        csize;
  1230.     short        h, v;
  1231.     RgnHandle    rgn;
  1232.  
  1233.     if (!listHndl) return;
  1234.  
  1235.     dataBnds = (*listHndl)->dataBounds;
  1236.     if ((*col < dataBnds.left) || (*col >= dataBnds.right))
  1237.         *col = leftEdge;
  1238.     if (*row < dataBnds.top)
  1239.         *row = dataBnds.top;
  1240.     if (*row >= dataBnds.bottom) {
  1241.         *row = -1;
  1242.         return;
  1243.     }
  1244.  
  1245.     keepView = (*listHndl)->rView;
  1246.     csize    = (*listHndl)->cellSize;
  1247.     keepVis  = (*listHndl)->visible;
  1248.  
  1249.     h = (drawRct->right - drawRct->left) / csize.h;
  1250.     if (!h)
  1251.         ++h;
  1252.     v = (drawRct->bottom - drawRct->top) / csize.v;
  1253.     if (!v)
  1254.         ++v;
  1255.  
  1256.     if (*col + h > dataBnds.right)
  1257.         h = dataBnds.right  - *col;
  1258.     if (*row + v > dataBnds.bottom)
  1259.         v = dataBnds.bottom - *row;
  1260.  
  1261.     drawRct->bottom = drawRct->top + v * csize.v;
  1262.  
  1263.     (*listHndl)->rView = *drawRct;
  1264.     (*listHndl)->visible.right  = ((*listHndl)->visible.left = *col) + h;
  1265.     (*listHndl)->visible.bottom = ((*listHndl)->visible.top  = *row) + v;
  1266.  
  1267.     if (!(rgn = clipRgn)) {
  1268.         rgn = NewRgn();
  1269.         RectRgn(rgn, drawRct);
  1270.     }
  1271.     CLUpdate(rgn, listHndl);
  1272.     if (!clipRgn)
  1273.         DisposeRgn(rgn);
  1274.  
  1275.     (*listHndl)->rView   = keepView;
  1276.     (*listHndl)->visible = keepVis;
  1277.  
  1278.     *col += h;
  1279.     if (*col >= dataBnds.right) {
  1280.         *col = leftEdge;
  1281.         *row += v;
  1282.         if (*row >= dataBnds.bottom)
  1283.             *row = -1;
  1284.     }
  1285. }
  1286.  
  1287.  
  1288.  
  1289. /*****************************************************************************/
  1290.  
  1291.  
  1292.  
  1293. /* Find the location in the list where the data would belong if inserted.  The row
  1294. ** and column are passed in.  If either is -1, that is the dimension that will be
  1295. ** determined and returned. */
  1296.  
  1297. #pragma segment ListControl
  1298. short    CLRowOrColSearch(ListHandle listHndl, void *data, short dataLen, short row, short col)
  1299. {
  1300.     ControlHandle    viewCtl;
  1301.     Rect            dataBnds;
  1302.     short            numCells, baseCell, pow, loc, cdataLen, varSize;
  1303.     Point            cell;
  1304.     Str255            cdata, gcdata;
  1305.     CLDataHndl        listData;
  1306.  
  1307.     if (!listHndl) return(-1);
  1308.  
  1309.     gGetCompareDataProc = nil;
  1310.     gDoCompareDataProc  = nil;
  1311.  
  1312.     varSize = 0;
  1313.     viewCtl = CLViewFromList(listHndl);
  1314.     if (viewCtl) {
  1315.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  1316.         if (listData) {
  1317.             gGetCompareDataProc = (*listData)->getCompareData;
  1318.             gDoCompareDataProc  = (*listData)->doCompareData;
  1319.             if ((*listData)->mode & clVariable) ++varSize;
  1320.         }
  1321.     }
  1322.  
  1323.     dataBnds = (*listHndl)->dataBounds;
  1324.     if (row == -1)
  1325.         numCells = dataBnds.bottom - (baseCell = dataBnds.top);
  1326.     else
  1327.         numCells = dataBnds.right  - (baseCell = dataBnds.left);
  1328.             /* Get some reference info on the size/start of the list. */
  1329.  
  1330.     if (varSize) {
  1331.         ++baseCell;
  1332.         --numCells;
  1333.     }
  1334.  
  1335.     cell.v = cell.h = varSize;
  1336.  
  1337.     for (;numCells > baseCell; --numCells) {        /* Exclude empty end cells. */
  1338.         if (row == -1)
  1339.             cell.v = baseCell + numCells - 1;
  1340.         else
  1341.             cell.h = baseCell + numCells - 1;
  1342.         cdataLen = 1;
  1343.         LGetCell(cdata, &cdataLen, cell, listHndl);
  1344.         if (cdataLen) break;
  1345.     }
  1346.  
  1347.     if (numCells) {
  1348.         if (row != -1)
  1349.             cell.v = row;
  1350.         if (col != -1)
  1351.             cell.h = col;
  1352.         for (pow = 1; pow < numCells; pow <<= 1) {};
  1353.         pow >>= 1;        /* pow = 2^n such that pow < numCells. */
  1354.  
  1355.         for (loc = pow; pow;) {            /* Do binary search for where to insert. */
  1356.             if (loc >= numCells)
  1357.                 loc = numCells - 1;        /* Off the end is bad. */
  1358.             if (row == -1)
  1359.                 cell.v = baseCell + loc;
  1360.             else
  1361.                 cell.h = baseCell + loc;
  1362.             pow >>= 1;
  1363.  
  1364.             cdataLen = 255;
  1365.             LGetCell(cdata, &cdataLen, cell, listHndl);        /* Get cell data to compare against. */
  1366.             if (gGetCompareDataProc) {
  1367.                 (*gGetCompareDataProc)(cdata, cdataLen, gcdata, &cdataLen);
  1368.                 BlockMove(gcdata, cdata, cdataLen);
  1369.             }
  1370.  
  1371.             if (gDoCompareDataProc)        /* Adjust location based on compare result. */
  1372.                 loc += (pow * (*gDoCompareDataProc)(data, cdata, dataLen, cdataLen));
  1373.             else
  1374.                 loc += (pow * IUMagString(data, cdata, dataLen, cdataLen));
  1375.         }
  1376.  
  1377.         /* The binary search got us close, but not exact.  We may be off by one
  1378.         ** in either direction.  (The binary search can't position in front of
  1379.         ** the first cell in the list, for example.)  Do a linear compare from
  1380.         ** this point to find the correct cell we should insert in front of. */
  1381.  
  1382.  
  1383.         if (loc < numCells) {                    /* Move to the first duplicate. */
  1384.             for (; --loc >= 0;) {
  1385.                 if (row == -1) cell.v = baseCell + loc;
  1386.                 else           cell.h = baseCell + loc;
  1387.                 cdataLen = 255;
  1388.                 LGetCell(cdata, &cdataLen, cell, listHndl);
  1389.                 if (gDoCompareDataProc) {
  1390.                     if ((*gDoCompareDataProc)(data, cdata, dataLen, cdataLen)) break;
  1391.                 }
  1392.                 else {
  1393.                     if (IUMagString(data, cdata, dataLen, cdataLen)) break;
  1394.                 }
  1395.             }
  1396.             ++loc;
  1397.         }
  1398.  
  1399.         if (loc) --loc;                /* Start linear search one back. */
  1400.         for (;; ++loc) {
  1401.             if (row == -1)
  1402.                 cell.v = baseCell + loc;
  1403.             else
  1404.                 cell.h = baseCell + loc;
  1405.             if (loc >= numCells) break;
  1406.             cdataLen = 255;
  1407.             LGetCell(cdata, &cdataLen, cell, listHndl);
  1408.             if (gGetCompareDataProc) {
  1409.                 (*gGetCompareDataProc)(cdata, cdataLen, gcdata, &cdataLen);
  1410.                 BlockMove(gcdata, cdata, cdataLen);
  1411.             }
  1412.  
  1413.             if (gDoCompareDataProc) {
  1414.                 if ((*gDoCompareDataProc)(data, cdata, dataLen, cdataLen) < 1) break;
  1415.             }
  1416.             else {
  1417.                 if (IUMagString(data, cdata, dataLen, cdataLen) < 1) break;
  1418.             }        /* If we are less than or equal to this cell, we have
  1419.                     ** found our insertion point, so break. */
  1420.         }
  1421.     }
  1422.  
  1423.     if (row == -1) return(cell.v);
  1424.     else           return(cell.h);
  1425. }
  1426.  
  1427.  
  1428.  
  1429. /*****************************************************************************/
  1430.  
  1431.  
  1432.  
  1433. /* Draw the List control in the correct form. */
  1434.  
  1435. #pragma segment ListControl
  1436. void    CLUpdate(RgnHandle clipRgn, ListHandle listHndl)
  1437. {
  1438.     WindowPtr        curPort, listPort;
  1439.     ControlHandle    ctl;
  1440.     Rect            view, vis, bnds, r;
  1441.     RgnHandle        rgn1, rgn2;
  1442.     CLDataHndl        listData;
  1443.     short            mode;
  1444.  
  1445.     if (listHndl) {
  1446.  
  1447.         ctl = CLViewFromList(listHndl);
  1448.         if (ctl) {
  1449.  
  1450.             GetPort(&curPort);
  1451.  
  1452.             listPort = (*listHndl)->port;
  1453.             (*listHndl)->port = curPort;
  1454.  
  1455.             listData = (CLDataHndl)(*ctl)->contrlData;
  1456.             mode     = (*listData)->mode;
  1457.             if (mode & clVariable) (*gclvUpdate)(clipRgn, listHndl);
  1458.             else                   LUpdate(clipRgn, listHndl);
  1459.  
  1460.             (*listHndl)->port = listPort;
  1461.  
  1462.             view = (*listHndl)->rView;
  1463.             vis  = (*listHndl)->visible;
  1464.             bnds = (*listHndl)->dataBounds;
  1465.             SectRect(&vis, &bnds, &vis);
  1466.  
  1467.             r = view;
  1468.             if (mode & clVariable) {
  1469.                 if ((--vis.right > 0) && (--vis.bottom > 0)) {
  1470.                     (*gclvGetCellRect)(listHndl, vis.right, vis.bottom, &r);
  1471.                     r.top  = view.top;
  1472.                     r.left = view.left;
  1473.                 }
  1474.                 else SetRect(&r, 0, 0, 0, 0);
  1475.             }
  1476.             else {
  1477.                 r.right  = r.left + (vis.right  - vis.left) * (*listHndl)->cellSize.h;
  1478.                 r.bottom = r.top  + (vis.bottom - vis.top ) * (*listHndl)->cellSize.v;
  1479.             }
  1480.             SectRect(&r, &view, &r);
  1481.  
  1482.             RectRgn(rgn1 = NewRgn(), &view);
  1483.             RectRgn(rgn2 = NewRgn(), &r);
  1484.             DiffRgn(rgn1, rgn2, rgn1);
  1485.             EraseRgn(rgn1);
  1486.             DisposeRgn(rgn1);
  1487.             DisposeRgn(rgn2);
  1488.         }
  1489.     }
  1490. }
  1491.  
  1492.  
  1493.  
  1494. /*****************************************************************************/
  1495.  
  1496.  
  1497.  
  1498. /* Return the control handle for the view control that owns the List
  1499. ** record.  Use this to find the view to do customizations such as changing
  1500. ** the update procedure for this List control. */
  1501.  
  1502. #pragma segment ListControl
  1503. ControlHandle    CLViewFromList(ListHandle listHndl)
  1504. {
  1505.     WindowPtr        window;
  1506.     ControlHandle    viewCtl;
  1507.     ListHandle        list;
  1508.  
  1509.     if (!listHndl) return(nil);
  1510.  
  1511.     if (gVFLView)
  1512.         if (gVFLList == listHndl)
  1513.             return(gVFLView);
  1514.  
  1515.     window = (WindowPtr)(*listHndl)->port;
  1516.     for (viewCtl = nil;;) {
  1517.         viewCtl = CLNext(window, &list, viewCtl, 1, false);
  1518.         if ((!viewCtl) || (list == listHndl)) return(viewCtl);
  1519.     }
  1520. }
  1521.  
  1522.  
  1523.  
  1524. static ControlHandle    dummyCLViewFromList(ListHandle listHndl)
  1525. {
  1526. #ifndef __MWERKS__
  1527. #pragma unused (listHndl)
  1528. #endif
  1529.     return(nil);
  1530. }
  1531.  
  1532.  
  1533.  
  1534. /*****************************************************************************/
  1535.  
  1536.  
  1537.  
  1538. /* This window is becoming active or inactive.  The borders of the List
  1539. ** controls need to be redrawn due to this.  For each List control in the
  1540. ** window, redraw the active border. */
  1541.  
  1542. #pragma segment ListControl
  1543. ListHandle    CLWindActivate(WindowPtr window, Boolean displayIt)
  1544. {
  1545.     ListHandle    list;
  1546.  
  1547.     gLastKeyTime = 0;        /* Restart the entry collection for Lists. */
  1548.  
  1549.     if (!window) return(nil);
  1550.  
  1551.     list = CLFindActive(window);
  1552.     if (list) {
  1553.         if (displayIt)
  1554.             CLBorderDraw(list);
  1555.         return(list);
  1556.     }
  1557.  
  1558.     return(nil);
  1559. }
  1560.  
  1561.  
  1562.  
  1563. static ListHandle    dummyCLWindActivate(WindowPtr window, Boolean displayIt)
  1564. {
  1565. #ifndef __MWERKS__
  1566. #pragma unused (window, displayIt)
  1567. #endif
  1568.  
  1569.     return(nil);
  1570. }
  1571.  
  1572.  
  1573.  
  1574. /*****************************************************************************/
  1575.  
  1576.  
  1577.  
  1578. #pragma segment ListControl
  1579. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  1580. {
  1581.     short    cmp;
  1582.     char    cstr[256];
  1583.  
  1584.     if (gGetCompareDataProc) {
  1585.         (*gGetCompareDataProc)(aPtr, aLen, cstr, &aLen);
  1586.         aPtr = cstr;
  1587.     }
  1588.  
  1589.     if (gDoCompareDataProc)
  1590.         cmp = (*gDoCompareDataProc)(aPtr, bPtr, aLen, bLen);
  1591.     else
  1592.         cmp = IUMagString(aPtr, bPtr, aLen, bLen);
  1593.  
  1594.     if (cmp == -1) return(1);
  1595.     else           return(0);
  1596. }
  1597.  
  1598.  
  1599.  
  1600. /*****************************************************************************/
  1601.  
  1602.  
  1603.  
  1604. #pragma segment ListControl
  1605. void    CLSize(ListHandle list, short newH, short newV)
  1606. {
  1607. #ifndef __MWERKS__
  1608. #pragma unused (oldh, oldv)
  1609. #endif
  1610.  
  1611.     WindowPtr        oldPort;
  1612.     ControlHandle    viewCtl, ctl;
  1613.     Rect            viewRct, rct;
  1614.     RgnHandle        oldClip, newClip;
  1615.     CLDataHndl        listData;
  1616.     short            mode, hScroll, vScroll;
  1617.  
  1618.     if (!(viewCtl = CLViewFromList(list))) return;
  1619.  
  1620.     GetPort(&oldPort);
  1621.     SetPort((*list)->port);
  1622.  
  1623.     viewRct  = (*viewCtl)->contrlRect;
  1624.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  1625.     mode     = (*listData)->mode;
  1626.  
  1627.     GetClip(oldClip = NewRgn());
  1628.     SetClip(newClip = NewRgn());
  1629.         /* Prevent any drawing, since the newly resized scrollbars may not account for
  1630.         ** the growIcon area.  We don't want them temporarily redrawing over the
  1631.         ** growIcon. */
  1632.  
  1633.     LSize(newH, newV, list);
  1634.  
  1635.     hScroll = (mode & clHScroll) ? 1 : 0;
  1636.     vScroll = (mode & clVScroll) ? 1 : 0;
  1637.         /* Get enough info to determine if we need to fix the scrollbar sizes. */
  1638.  
  1639.     viewRct.right  = viewRct.left + newH;
  1640.     viewRct.bottom = viewRct.top  + newV;
  1641.     (*viewCtl)->contrlRect = viewRct;
  1642.  
  1643.     if ((mode & clHasGrow) && (hScroll + vScroll == 1)) {
  1644.         ctl = (*list)->hScroll;
  1645.         if (ctl) {
  1646.             rct = (*ctl)->contrlRect;
  1647.             if (rct.right >= viewRct.right)
  1648.                 SizeControl(ctl, viewRct.right - viewRct.left - 13, rct.bottom - rct.top);
  1649.         }
  1650.         ctl = (*list)->vScroll;
  1651.         if (ctl) {
  1652.             rct = (*ctl)->contrlRect;
  1653.             if (rct.bottom >= viewRct.bottom)
  1654.                 SizeControl(ctl, rct.right - rct.left, viewRct.bottom - viewRct.top - 13);
  1655.         }
  1656.     }
  1657.  
  1658.     SetClip(oldClip);
  1659.     DisposeRgn(oldClip);
  1660.     DisposeRgn(newClip);
  1661.         /* Allow drawing again so we can redisplay the newly resized list control and scrollbars. */
  1662.  
  1663.     InsetRect(&viewRct, -1, -1);        /* Erase all of the old list control and scrollbars. */
  1664.     if (vScroll)
  1665.         viewRct.right += 15;
  1666.     if (hScroll)
  1667.         viewRct.bottom += 15;
  1668.     if (mode & clShowActive)
  1669.         InsetRect(&viewRct, -4, -4);
  1670.     if ((*viewCtl)->contrlVis)
  1671.         EraseRect(&viewRct);
  1672.  
  1673.     if (mode & clVariable) (*gclvAdjustScrollBars)(list);
  1674.  
  1675.     DoDraw1Control(viewCtl, false);
  1676.     SetPort(oldPort);
  1677. }
  1678.  
  1679.  
  1680.  
  1681. /*****************************************************************************/
  1682.  
  1683.  
  1684.  
  1685. #pragma segment ListControl
  1686. void    CLMove(ListHandle list, short newH, short newV)
  1687. {
  1688. #ifndef __MWERKS__
  1689. #pragma unused (oldh, oldv)
  1690. #endif
  1691.  
  1692.     WindowPtr        oldPort;
  1693.     ControlHandle    viewCtl, ctl;
  1694.     Rect            viewRct, rct;
  1695.     RgnHandle        oldClip, newClip;
  1696.     CLDataHndl        listData;
  1697.     short            mode, hScroll, vScroll, dx, dy;
  1698.  
  1699.     if (!(viewCtl = CLViewFromList(list))) return;
  1700.  
  1701.     GetPort(&oldPort);
  1702.     SetPort((*list)->port);
  1703.  
  1704.     viewRct  = (*viewCtl)->contrlRect;
  1705.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  1706.     mode     = (*listData)->mode;
  1707.  
  1708.     dx = newH - viewRct.left;
  1709.     dy = newV - viewRct.top;
  1710.     if ((!dx) && (!dy)) return;
  1711.  
  1712.     hScroll = (mode & clHScroll) ? 1 : 0;
  1713.     vScroll = (mode & clVScroll) ? 1 : 0;
  1714.         /* Get enough info to determine if we need to fix the scrollbar sizes. */
  1715.  
  1716.     rct = viewRct;                /* Erase all of the old list control and scrollbars. */
  1717.     InsetRect(&rct, -1, -1);
  1718.     if (vScroll)
  1719.         rct.right += 15;
  1720.     if (hScroll)
  1721.         rct.bottom += 15;
  1722.     if (mode & clShowActive)
  1723.         InsetRect(&rct, -4, -4);
  1724.     if ((*viewCtl)->contrlVis)
  1725.         EraseRect(&rct);
  1726.  
  1727.     OffsetRect(&viewRct, dx, dy);
  1728.     (*viewCtl)->contrlRect = viewRct;
  1729.     (*list)->rView = viewRct;
  1730.  
  1731.     GetClip(oldClip = NewRgn());
  1732.     SetClip(newClip = NewRgn());
  1733.  
  1734.     ctl = (*list)->hScroll;
  1735.     if (ctl) {
  1736.         rct = (*ctl)->contrlRect;
  1737.         MoveControl(ctl, rct.left + dx, rct.top + dy);
  1738.     }
  1739.  
  1740.     ctl = (*list)->vScroll;
  1741.     if (ctl) {
  1742.         rct = (*ctl)->contrlRect;
  1743.         MoveControl(ctl, rct.left + dx, rct.top + dy);
  1744.     }
  1745.  
  1746.     SetClip(oldClip);
  1747.     DisposeRgn(oldClip);
  1748.     DisposeRgn(newClip);
  1749.  
  1750.     DoDraw1Control(viewCtl, false);
  1751.     SetPort(oldPort);
  1752. }
  1753.  
  1754.  
  1755.  
  1756. /*****************************************************************************/
  1757.  
  1758.  
  1759.  
  1760. /* Show the designated List control and related scrollbars. */
  1761.  
  1762. #pragma segment ListControl
  1763. void    CLShow(ListHandle list)
  1764. {
  1765.     ControlHandle    viewCtl, scrollCtl;
  1766.     short            i;
  1767.  
  1768.     viewCtl = CLViewFromList(list);
  1769.     if (viewCtl) {
  1770.         ShowStyledControl(viewCtl);
  1771.         for (i = 0; i < 2; i++) {
  1772.             scrollCtl = (i) ? (*list)->hScroll : (*list)->vScroll;
  1773.             if (scrollCtl)
  1774.                 ShowStyledControl(scrollCtl);
  1775.         }
  1776.     }
  1777. }
  1778.  
  1779.  
  1780.  
  1781. /*****************************************************************************/
  1782.  
  1783.  
  1784.  
  1785. /* Hide the designated List control and related scrollbars. */
  1786.  
  1787. #pragma segment ListControl
  1788. Rect    CLHide(ListHandle list)
  1789. {
  1790.     ControlHandle    viewCtl, scrollCtl;
  1791.     short            i;
  1792.     WindowPtr        oldPort;
  1793.     CLDataHndl        listData;
  1794.     short            displayInfo;
  1795.     Rect            rct;
  1796.  
  1797.     viewCtl = CLViewFromList(list);
  1798.     if (viewCtl) {
  1799.         HideControl(viewCtl);
  1800.         for (i = 0; i < 2; i++) {
  1801.             scrollCtl = (i) ? (*list)->hScroll : (*list)->vScroll;
  1802.             if (scrollCtl)
  1803.                 HideControl(scrollCtl);
  1804.         }
  1805.     }
  1806.  
  1807.     GetPort(&oldPort);
  1808.     SetPort((*list)->port);
  1809.     listData    = (CLDataHndl)(*viewCtl)->contrlData;
  1810.     displayInfo = (*listData)->mode;
  1811.     rct         = (*list)->rView;
  1812.     InsetRect(&rct, -1, -1);
  1813.     if ((*list)->vScroll)
  1814.         rct.right  += 15;
  1815.     if ((*list)->hScroll)
  1816.         rct.bottom += 15;
  1817.     if (displayInfo & clActive)
  1818.         if (displayInfo & clShowActive)
  1819.             InsetRect(&rct, -3, -3);
  1820.  
  1821.     EraseRect(&rct);
  1822.     SetPort(oldPort);
  1823.  
  1824.     return(rct);
  1825. }
  1826.  
  1827.  
  1828.  
  1829.